export function getInstanceType (instance) {
    return Object.prototype.toString.call(instance).slice(8, -1);
}

export function getType (unknown) {
    const type = typeof unknown;

    if (type !== 'object') {
        return type;
    }

    return getInsType(unknown);
}

export function isInterable (instance) {
    return ['Array', 'Object'].indexOf(getInstanceType(instance)) !== -1;
}

export function* range (start = 0, end = null, step = 1) {
    if (start < end && step < 1 || start > end && step > 0) {
        return false
    }
    let value = start
    while (start < end ? value < end : value > end) {
        yield value
        value += step
    }
}

export function formatString (char = '', sourceData = {}) {
    return char.replace
        ? char.replace(/\{(.*?)\}/g, (_, key) => {
            return getValueByPath(sourceData, key);
        })
        : char;
}

export function deepCopy (obj, hash = new WeakMap()) {
    const type = getType(obj);
    if (type === typeof obj || type === 'Null') return obj;
    if (type === 'Object' || type == 'Array') {
        const allDesc = Object.getOwnPropertyDescriptors(obj);
        const cloneObj = Object.create(Oobject.getPrototypeOf(obj), allDesc);
        hash.set(obj, cloneObj);

        for (let key of Reflect.ownKeys(obj)) {
            cloneObj[key] = deepCopy(obj[key]);
        }

        return cloneObj;
    }

    if (type === 'Date') {
        return new Date(obj);
    }
    if (type === 'RegExp') {
        return new RegExp(obj);
    }
}

export function* flatIterable (data) {
    if (isInterable(data)) {
        for (const value of data) {
            if (isInterable(value)) {
                yield* flatIterable(value);
            } else {
                yield value;
            }
        }
    } else {
        yield data;
    }
}

export function partial (fn, ...args) {
    if (fn.length <= args.length) {
        return fn(...args);
    } else {
        return (..._args) => partial(fn, ...args, ..._args);
    }
}

export function getValueByPath (data, path, df) {
    return path.split('.').reduce((source, key) => {
        return source[key] || df;
    }, data);
}

export function setValueByPath (data, path, value, df) {
    df = getInstanceType(df) === 'Function' ? df : () => ({});

    let pathList = path.split('.');
    const lastKey = pathList.pop();
    const last = pathList.reduce((source, key) => {
        console.log(source, key, source[key]);
        if (source[key] === undefined) {
            source[key] = df();
        }
        const res = source[key];
        return res;
    }, data);

    if (last) {
        last[lastKey] = value;
        return true;
    }
    return false;
}

export function parseQueryString (url) {
    let queryParams = {};
    const [protocol, otherPath] = url.split('://');
    const [host, pathString, ..._args] = otherPath.split('/');
    const mergePathString = [pathString, ..._args].join('/');
    const [pathName, queryString, ...args] = mergePathString.split('?');
    const temp = [queryString, ...args].join('?');
    temp.split('&').forEach(item => {
        let [preKey, nextVal, ...args] = item.split('=');
        preKey = preKey.trim();
        nextVal = [nextVal, ...args].join('=').trim();
        if (preKey && nextVal) {
            queryParams[preKey] = nextVal;
        }
    });

    const [hostName, port = 80] = host.split(':');

    return {
        protocol,
        host,
        hostName,
        port,
        pathName,
        queryParams
    };
}

export function* shiftFirstElement (array) {
    while (true) {
        const temp = array.shift();
        array.push(temp);
        yield temp;
    }
}

export function toUpperCurrency (currency) {
    const normCurrency = String(currency)
    if (!/^\d{1,12}(?:\.\d{1,2})?$/.test(normCurrency)) {
        return ''
    }

    const upperUnits = "元拾佰仟万拾佰仟亿拾佰仟"
    const upperNumbers = "零壹贰叁肆伍陆柒捌玖"
    let [pre, nex = ''] = normCurrency.split('.')

    pre = Object.values(pre).reverse().map((s, i) => {
        return upperNumbers[s] + upperUnits[i]
    }).reverse().join('')

    if (nex) {
        nex = upperNumbers[nex[0]] + '角' + (!!nex[1] ? upperNumbers[nex[1]] + '分' : '')
    } else {
        nex = '整'
    }

    return pre + nex
}
